from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from sqlalchemy import func
from typing import List, Dict, Optional
from ..db import get_db
from .. import models, schemas
from ..security import get_current_user

router = APIRouter()

def _max_order(db: Session, owner_id: int, parent_id: Optional[int]) -> int:
    q = db.query(models.ReviewFolder.order).filter(models.ReviewFolder.owner_id == owner_id)
    if parent_id is None:
        q = q.filter(models.ReviewFolder.parent_id == None)  # noqa: E711
    else:
        q = q.filter(models.ReviewFolder.parent_id == parent_id)
    row = q.order_by(models.ReviewFolder.order.desc()).first()
    return (row[0] if row else -1)

def _is_descendant(db: Session, owner_id: int, possible_ancestor_id: int, target_id: int) -> bool:
    items = db.query(models.ReviewFolder).filter(models.ReviewFolder.owner_id == owner_id).all()
    table: Dict[int, list[int]] = {}
    for n in items:
        table.setdefault(n.parent_id or 0, []).append(n.id)
    stack = table.get(possible_ancestor_id, [])[:]
    while stack:
        nid = stack.pop()
        if nid == target_id:
            return True
        stack.extend(table.get(nid, []))
    return False

@router.post("/", response_model=schemas.FolderOut)
def create_folder(payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    next_order = _max_order(db, current.id, payload.parent_id) + 1
    f = models.ReviewFolder(name=payload.name, parent_id=payload.parent_id, owner_id=current.id, order=next_order)
    db.add(f)
    db.commit()
    db.refresh(f)
    return f

# alias without trailing slash to avoid 307 redirect
@router.post("", response_model=schemas.FolderOut)
def create_folder_noslash(payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    return create_folder(payload, db, current)

@router.get("/", response_model=List[schemas.FolderOut])
def list_folders(db: Session = Depends(get_db), current=Depends(get_current_user)):
    return db.query(models.ReviewFolder).filter(models.ReviewFolder.owner_id == current.id).order_by(models.ReviewFolder.parent_id, models.ReviewFolder.order, models.ReviewFolder.id).all()

# alias without trailing slash to avoid 307 redirect
@router.get("", response_model=List[schemas.FolderOut])
def list_folders_noslash(db: Session = Depends(get_db), current=Depends(get_current_user)):
    return list_folders(db, current)

@router.get("/tree")
def tree(db: Session = Depends(get_db), current=Depends(get_current_user)):
    items = db.query(models.ReviewFolder).filter(models.ReviewFolder.owner_id == current.id).order_by(models.ReviewFolder.parent_id, models.ReviewFolder.order, models.ReviewFolder.id).all()
    counts_rows = db.query(models.ReviewFolderLink.folder_id, func.count(models.ReviewFolderLink.id)).group_by(models.ReviewFolderLink.folder_id).all()
    counts: Dict[int, int] = {fid: cnt for fid, cnt in counts_rows}
    nodes: Dict[int, dict] = {f.id: {"id": f.id, "name": f.name, "parent_id": f.parent_id, "count": counts.get(f.id, 0), "children": []} for f in items}
    roots = []
    for f in items:
        if f.parent_id and f.parent_id in nodes:
            nodes[f.parent_id]["children"].append(nodes[f.id])
        else:
            roots.append(nodes[f.id])
    def dfs_sum(n: dict) -> int:
        total = int(n.get("count", 0))
        for c in n.get("children", []):
            total += dfs_sum(c)
        n["count"] = total
        return total
    for r in roots:
        dfs_sum(r)
    return roots

@router.put("/{folder_id}", response_model=schemas.FolderOut)
def update_folder(folder_id: int, payload: schemas.FolderCreate, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.ReviewFolder).filter(models.ReviewFolder.id == folder_id, models.ReviewFolder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    f.name = payload.name
    f.parent_id = payload.parent_id
    db.commit()
    db.refresh(f)
    return f

@router.delete("/{folder_id}")
def delete_folder(folder_id: int, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.ReviewFolder).filter(models.ReviewFolder.id == folder_id, models.ReviewFolder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    db.query(models.ReviewFolderLink).filter(models.ReviewFolderLink.folder_id == folder_id).delete()
    db.delete(f)
    db.commit()
    return {"ok": True}

@router.patch("/reorder/{folder_id}", response_model=schemas.FolderOut)
def reorder_folder(folder_id: int, body: schemas.FolderReorder, db: Session = Depends(get_db), current=Depends(get_current_user)):
    f = db.query(models.ReviewFolder).filter(models.ReviewFolder.id == folder_id, models.ReviewFolder.owner_id == current.id).first()
    if not f:
        raise HTTPException(status_code=404, detail="Folder not found")
    new_parent = body.parent_id
    if new_parent == folder_id:
        raise HTTPException(status_code=400, detail="Cannot set folder as its own parent")
    if new_parent and _is_descendant(db, current.id, folder_id, new_parent):
        raise HTTPException(status_code=400, detail="Cannot move folder under its descendant")

    f.parent_id = new_parent

    if body.before_id:
        before = db.query(models.ReviewFolder).filter(models.ReviewFolder.id == body.before_id, models.ReviewFolder.owner_id == current.id).first()
        if not before:
            raise HTTPException(status_code=404, detail="before_id not found")
        if before.parent_id != new_parent:
            raise HTTPException(status_code=400, detail="before_id must be sibling in target parent")
        db.query(models.ReviewFolder).filter(
            models.ReviewFolder.owner_id == current.id,
            (models.ReviewFolder.parent_id == new_parent) | ((new_parent is None) & (models.ReviewFolder.parent_id == None)),  # noqa: E711
            models.ReviewFolder.order >= before.order,
        ).update({models.ReviewFolder.order: models.ReviewFolder.order + 1}, synchronize_session=False)
        f.order = before.order
    elif body.after_id:
        after = db.query(models.ReviewFolder).filter(models.ReviewFolder.id == body.after_id, models.ReviewFolder.owner_id == current.id).first()
        if not after:
            raise HTTPException(status_code=404, detail="after_id not found")
        if after.parent_id != new_parent:
            raise HTTPException(status_code=400, detail="after_id must be sibling in target parent")
        f.order = after.order + 1
        db.query(models.ReviewFolder).filter(
            models.ReviewFolder.owner_id == current.id,
            (models.ReviewFolder.parent_id == new_parent) | ((new_parent is None) & (models.ReviewFolder.parent_id == None)),  # noqa: E711
            models.ReviewFolder.order >= f.order,
        ).update({models.ReviewFolder.order: models.ReviewFolder.order + 1}, synchronize_session=False)
    else:
        f.order = _max_order(db, current.id, new_parent) + 1

    db.commit()
    db.refresh(f)
    return f
